package com.bs.utils;

import com.alibaba.fastjson.JSONObject;
import org.apache.http.HttpEntity;
import org.apache.http.HttpHost;
import org.apache.http.HttpMessage;
import org.apache.http.NameValuePair;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.CredentialsProvider;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.TrustStrategy;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.*;
import org.apache.http.impl.conn.DefaultProxyRoutePlanner;
import org.apache.http.message.BasicHeader;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.ssl.SSLContextBuilder;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.net.ssl.SSLContext;
import java.net.URI;
import java.net.URISyntaxException;
import java.security.KeyManagementException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Optional;

/**
 * HttpClient发送GET、POST请求
 */
public class XHttpClientUtil {

    private static final Logger LOGGER = LoggerFactory.getLogger(XHttpClientUtil.class);

    private static final int SUCCESS_CODE = 200;

    /**
     * @param url
     * @param headers
     * @param nameValuePairList
     * @return
     * @throws Exception
     */
    public static Object sendGet(Optional<String> proxyUrl,
                                 Optional<String> userName,
                                 Optional<String> password,
                                 String url,
                                 Map<String, String> headers,
                                 List<NameValuePair> nameValuePairList) throws Exception {
        JSONObject jsonObject = null;
        CloseableHttpClient client = null;
        CloseableHttpResponse response = null;
        try {
            client = createXCloseableHttpClient(proxyUrl, userName, password);
            //client = HttpClients.createDefault();
            URIBuilder uriBuilder = new URIBuilder(url);
            uriBuilder.addParameters(nameValuePairList);
            HttpGet get = new HttpGet(uriBuilder.build());
            setCommonHeaders(get);
            for (Map.Entry<String, String> entry : headers.entrySet()) {
                get.setHeader(new BasicHeader(entry.getKey(), entry.getValue()));
            }
            response = client.execute(get);
            int statusCode = response.getStatusLine().getStatusCode();
            if (SUCCESS_CODE == statusCode) {
                HttpEntity entity = response.getEntity();
                String result = EntityUtils.toString(entity, "UTF-8");
                try {
                    jsonObject = JSONObject.parseObject(result);
                    return jsonObject;
                } catch (Exception e) {
                    return result;
                }
            } else {
                LOGGER.error("HttpClientUtils-line: {}, errorMsg{}", 97, "GET请求失败！");
            }
        } catch (Exception e) {
            LOGGER.error("HttpClientUtils-line: {}, Exception: {}", 100, e);
        } finally {
            response.close();
            client.close();
        }
        return null;
    }

    /**
     * @param url
     * @param headers
     * @param nameValuePairList
     * @return
     * @throws Exception
     */
    public static Object sendGet(String url,
                                 Map<String, String> headers,
                                 List<NameValuePair> nameValuePairList) throws Exception {
        return sendGet(Optional.empty(), Optional.empty(), Optional.empty(), url, headers, nameValuePairList);
    }

    /**
     * @param proxyUrl          like http://192.1.2.3:8081 or null
     * @param url
     * @param headers
     * @param nameValuePairList
     * @return
     * @throws Exception
     */
    public static Object sendPost(Optional<String> proxyUrl,
                                  Optional<String> userName,
                                  Optional<String> password,
                                  String url, Map<String, String> headers,
                                  List<NameValuePair> nameValuePairList) throws Exception {
        JSONObject jsonObject = null;
        CloseableHttpClient client = null;
        CloseableHttpResponse response = null;
        try {
//            client = createXCloseableHttpClient(proxyUrl, userName, password);
            client = HttpClients.createDefault();
            HttpPost post = new HttpPost(url);
            StringEntity entity = new UrlEncodedFormEntity(nameValuePairList, "UTF-8");
            post.setEntity(entity);
            setCommonHeaders(post);
            for (Map.Entry<String, String> entry : headers.entrySet()) {
                post.setHeader(new BasicHeader(entry.getKey(), entry.getValue()));
            }
            response = client.execute(post);
            int statusCode = response.getStatusLine().getStatusCode();
            if (SUCCESS_CODE == statusCode) {
                String result = EntityUtils.toString(response.getEntity(), "UTF-8");
                try {
                    jsonObject = JSONObject.parseObject(result);
                    return jsonObject;
                } catch (Exception e) {
                    return result;
                }
            } else {
                LOGGER.error("HttpClientUtils-line: {}, errorMsg：{}", 146, "POST请求失败！");
            }
        } catch (Exception e) {
            LOGGER.error("HttpClientUtils-line: {}, Exception：{}", 149, e);
        } finally {
            response.close();
            client.close();
        }
        return null;
    }


    /**
     * @param url
     * @param headers
     * @param nameValuePairList
     * @throws Exception
     */
    public static Object sendPost(String url,
                                  Map<String, String> headers,
                                  List<NameValuePair> nameValuePairList) throws Exception {
        return sendPost(Optional.empty(), Optional.empty(), Optional.empty(), url, headers, nameValuePairList);
    }


    /**
     * @param proxyUrl like http://192.1.2.3:8081 or null
     * @return
     */
    public static CloseableHttpClient createXCloseableHttpClient(Optional<String> proxyUrl,
                                                                 Optional<String> userName,
                                                                 Optional<String> password
    ) {
        CloseableHttpClient client = null;
        if (proxyUrl.isPresent()) {
            try {
                URI proxy = new URI(proxyUrl.get());
                client = createXCloseableHttpClient(Optional.of(proxy.getHost()), Optional.of(proxy.getPort()), userName, password);
            } catch (URISyntaxException e) {
                LOGGER.error(e.getLocalizedMessage());
                client = null;
            }
        }
        return client;
    }

    /**
     * @param host proxy host
     * @param port
     * @return
     */
    public static CloseableHttpClient createXCloseableHttpClient(Optional<String> host,
                                                                 Optional<Integer> port,
                                                                 Optional<String> userName,
                                                                 Optional<String> password) {
        String scheme = "http";
        HttpClientBuilder clientBuilder = HttpClients.custom()
                .setDefaultRequestConfig(defaultRequestConfig())
                .setRetryHandler(new DefaultHttpRequestRetryHandler(3, true));
        //
        if (host.isPresent() && port.isPresent()) {
            HttpHost proxy = new HttpHost(host.get(), port.get(), scheme);
            DefaultProxyRoutePlanner routePlanner = new DefaultProxyRoutePlanner(proxy);
            clientBuilder.setRoutePlanner(routePlanner);
            //
            if (userName.isPresent() && password.isPresent()) {
                CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
                credentialsProvider.setCredentials(new AuthScope(AuthScope.ANY_HOST, AuthScope.ANY_PORT),
                        new UsernamePasswordCredentials(userName.get(), password.get()));
                clientBuilder.setDefaultCredentialsProvider(credentialsProvider);
            }
        }
        return clientBuilder.build();
    }

    /**
     * @return
     */
    public static RequestConfig defaultRequestConfig() {
        RequestConfig requestConfig = RequestConfig.custom()
                .setSocketTimeout(5000)
                .setConnectTimeout(5000)
                .setConnectionRequestTimeout(5000)
                .setStaleConnectionCheckEnabled(true)
                .build();
        return requestConfig;
    }


    /**
     * @param params
     * @param values
     * @return
     */
    public static List<NameValuePair> getParams(Object[] params, Object[] values) {
        boolean flag = params.length > 0 && values.length > 0 && params.length == values.length;
        if (flag) {
            List<NameValuePair> nameValuePairList = new ArrayList<>();
            for (int i = 0; i < params.length; i++) {
                nameValuePairList.add(new BasicNameValuePair(params[i].toString(), values[i].toString()));
            }
            return nameValuePairList;
        } else {
            LOGGER.error("HttpClientUtils-line: {}, errorMsg：{}", 197, "请求参数为空且参数长度不一致");
        }
        return null;
    }

    /**
     * @param message
     */
    private static void setCommonHeaders(HttpMessage message) {
        message.setHeader(new BasicHeader("Content-Type", "application/x-www-form-urlencoded; charset=utf-8"));
        message.setHeader(new BasicHeader("Accept", "text/plain;charset=utf-8"));
    }
}